﻿using pdfer.FileObjects;
using pdfer.Filters;
using pdfer.PdfObjecs;
using pdfer.PdfObjecs.Fonts;
using pdfer.Renders;
using pdfer.Syntax;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using System.Collections;

namespace pdfer
{
    /// <summary>
    /// 如何只保存当前页的资源和公共资源
    /// 后续只做提供对象的功能
    /// </summary>
    internal class PdfParser
    {
        private readonly Stream stream;
        private readonly String? password;
        private String version;
        private TrailerDictionary pdfFileTrailer;
        private XRefTable[] xrefTables;
        private Dictionary<String, (Int32, Int32)> xrefMap;
        //TODO 分页的对象  公共的对象
        private Dictionary<String, PdfFileObject> fileObjects;
        private PagesDictionary documentTree;
        private List<PageDictionary> pageItems;

        internal PagesDictionary DocumentTree 
        {
            get 
            {
                if (documentTree == null) 
                {
                    // Tailer.Root > DocCatalog.Pages > Pages.Kids
                    var rootId = TrailerDictionary.Root;
                    if (rootId != null)
                    {
                        var docCatalog = GetPdfFileObject<DocCatalogDictionary>(rootId);
                        if (docCatalog != null)
                        {
                            documentTree = GetPdfFileObject<PagesDictionary>(docCatalog.Pages);
                        }
                    }
                }
                return documentTree;
            }
        }
        internal TrailerDictionary TrailerDictionary
        {
            get
            {
                return pdfFileTrailer;
            }
        }

        internal List<PageDictionary> PageItems
        {
            get 
            {
                if(pageItems == null)
                {
                    pageItems = new List<PageDictionary>();
                    InitPageItems();
                }
                return pageItems; 
            }
        }

        private void InitPageItems()
        {
            // 读取 page 根节点
            var kids = DocumentTree.Kids;
            _ParsePagesKids(kids);
        }

        private void _ParsePagesKids(List<String> kids)
        {
            foreach (var kid in kids)
            {
                var item = GetPdfFileObject<PdfFileObject>(kid);
                if(item == null)
                {
                    // 应该报错
                    continue;
                }
                if (item.TryGetValue(PdfName.Key_Type, out var _type))
                {
                     var _str = _type.ToString();
                    if (_str == PdfTypeConst.Type_Pages)
                    {
                        var pagesItem = new PagesDictionary(item);
                        _ParsePagesKids(pagesItem.Kids);
                    }
                    else if (_str == PdfTypeConst.Type_Page)
                    {
                        var pageItem = new PageDictionary(item)
                        {
                            PageIndex = pageItems.Count
                        };
                        pageItems.Add(pageItem);
                    }
                }
            }
        }

        public PdfParser(Stream stream, String? password)
        {
            this.stream = stream;
            this.password = password;
            // 读取 xref及文件尾部字典信息
            InitLoadFileInfo();
        }

        /// <summary>
        /// 加载必要的文件信息 验证头部 加载 尾部信息及xref表
        /// </summary>
        private void InitLoadFileInfo()
        {
            stream.Position = 0;
            VerifyFileHeader();
            fileObjects = [];
            LoadXRefAndTails();
        }

        /// <summary>
        /// 完整过程<br>
        /// 读取 startxref
        /// 读取交叉引用表
        /// 读取紧邻的 trailer
        /// 如果 trailer中有Prev则跳转接着读，直到不存在Prev
        /// </summary>
        private void LoadXRefAndTails()
        {
            var (xrefInfo, mapInfo, trailerObj) = PdfParserUtils.ParseXRef2andTailerV3(stream);
            xrefTables = xrefInfo;
            xrefMap = mapInfo;
            pdfFileTrailer = trailerObj;
        }

        /// <summary>
        /// 验证文件头
        /// </summary>
        /// <param name="pdf"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        private unsafe void VerifyFileHeader()
        {
            var header = stackalloc Byte[4];
            var span = new Span<Byte>(header, 16);
            stream.Read(span);
            // 以%PDF开头
            if(span[0] == 0x25 && span[1] == 0x50 && span[2] == 0x44 && span[3] == 0x46)
            {
                // 读取版本
                var i = 4;
                while (true)
                {
                    if(span[i] == 0x0A || span[i] == 0x0D || span[i] == 0x25 || i > 15)
                    {
                        break;
                    }
                    i++;
                }
                version = PdfParserUtils.Encoding.GetString(span[5..i]);
                return;
            }
            // TODO 1. 不合法的文件
            throw new NotSupportedException("文件头不符合预期");
        }

        /// <summary>
        /// 读取指定的Id
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        internal T? GetPdfFileObject<T>(String id) where T : PdfFileObject
        {
            if (String.IsNullOrEmpty(id))
            {
                return null;
            }
            if(id.EndsWith(" R"))
            {
                id = id.TrimEnd('R').TrimEnd();
            }

            var flag = false;
            if(fileObjects.TryGetValue(id, out var obj))
            {
                if (obj is T res)
                {
                    return res;
                }
                flag = true;
                //return null;
            }
            if (xrefMap.TryGetValue(id, out var offset))
            {
                if(offset.Item2 > -1)
                {
                    // 对象可能在ObjStm中声明
                    var objStm = GetPdfFileObject<ObjStmDictionary>($"{offset.Item1} 0");
                    if(objStm != null)
                    {
                        obj = objStm.GetPdfFileObject(offset.Item2);
                    }
                }
                else
                {
                    obj = PdfParserUtils.ParseFileObject(stream, offset.Item1);
                }
            }
            else
            {
                throw new Exception($"未找到对象: {id}");
            }

            if (obj != null)
            {
                var type = typeof(T);
                obj.PdfParser = this;
                
                var ctor = type.GetConstructor([typeof(PdfFileObject)]);
                if(ctor != null)
                {
                    var _obj = ctor.Invoke([obj]);
                    obj = _obj as T;
                }

                
                if (flag)
                {
                    fileObjects[id]= obj;
                }
                else
                {
                    fileObjects.Add(id, obj);
                }
                return obj as T;
            }
            return null;
        }

        internal String Version {  get { return version; } }

        internal DocInfoDictionary GetDocumentInfo()
        {
            var info = GetPdfFileObject<DocInfoDictionary>(TrailerDictionary.Info);
            return info;
        }

        internal PdfBookmarkCollection GetBookmarkInfo()
        {
            var result = new PdfBookmarkCollection();
            // Tailer.Root > DocCatalog.Outlines 
            var catalog = GetPdfFileObject<DocCatalogDictionary>(TrailerDictionary.Root);
            if(catalog == null)
            {
                return result;
            }
            var rootOutline = GetPdfFileObject<OutlinesDictionary>(catalog.Outlines);
            if (rootOutline == null) 
            {
                return result;
            }
            result = InnerGetBookmarkInfo(rootOutline.First, rootOutline.Last);
            return result;
        }

        private PdfBookmarkCollection InnerGetBookmarkInfo(String firstId, String lastId)
        {
            var result = new PdfBookmarkCollection();
            var currentId = firstId.Trim();
            while (true)
            {
                var item = GetPdfFileObject<OutlinesDictionary>(currentId);
                if (item == null)
                {
                    // Todo 抛异常
                    break;
                }
                var _item = new PdfBookmark { PageIndex = 0, Title = item.Title };
                String dest = null;
                if(item.Dest == null)
                {
                    var action = GetPdfFileObject<PdfFileObject>(item.A);
                    if(action.TryGetValue(PdfName.Key_D, out var destObjs))
                    {
                        if(destObjs is IList destObjArr)
                        {
                            dest = destObjArr[0].ToString();
                        }
                    }
                }
                else
                {
                    dest = item.Dest[0].ToString();
                }
                if (String.IsNullOrEmpty(dest))
                {
                    continue;
                }
                var pageIndex = GetPageIndex(dest);
                if (pageIndex == -1)
                {
                    continue;
                }
                _item.PageIndex = pageIndex;


                result.Add(_item);
                if (!String.IsNullOrEmpty(item.First))
                {
                    PdfBookmarkCollection children = InnerGetBookmarkInfo(item.First, item.Last);
                    foreach (var item1 in children)
                    {
                        _item.Children.Add(item1);
                    }
                }
                if (String.IsNullOrEmpty(item.Next) || currentId == lastId)
                {
                    break;
                }
                currentId = item.Next;
            }
            return result;
        }

        private Int32 GetPageIndex(String id)
        {
            if (id.EndsWith('R'))
            {
                id = id[..^1].Trim();
            }
            // done 暂时没实现
            // 由于对象没有对于的页码编号所以需要按id出现的前后生成一个id与页码的对应关系
            return PageItems.FirstOrDefault(p => p.No == id)?.PageIndex ?? -1;
        }

        internal Byte[][] GetImages(Int32 page)
        {
            if(page < 0 || page >= PageItems.Count)
            {
                return [];
            }
            var pageDic = PageItems[page];
            //GetPdfFileObject<PageDictionary>(id);
            if (pageDic == null) 
            {
                return [];
            }
            var imgs = new List<Byte[]>();
            // 读取下面的对象
            // 找到对象类型是图片的对象

            if(pageDic.Resources.TryGetValue(PdfName.Key_XObject, out var xobjs))
            {
                if(xobjs != null)
                {
                    Dictionary<String, Object> xobjsdic = null;
                    if (xobjs is Dictionary<String, Object> _xobjsdic)
                    {
                        xobjsdic = _xobjsdic;
                    }
                    else if (xobjs is String refId && refId.EndsWith(" R"))
                    {
                        var xobjsobj = pageDic.GetIndirectObj(refId);
                        xobjsdic = xobjsobj.Dictionary;
                    }

                    if(xobjsdic != null)
                    {
                        foreach (var xobjref in xobjsdic.OrderBy(p => p.Key))
                        {
                            var xobj = GetPdfFileObject<XObjectDictionary>(xobjref.Value.ToString());
                            if (xobj != null && xobj.SubType == PdfTypeConst.Type_Image)
                            {
                                var imgData = xobj.DecodeStreamData;
                                #region Comments Mask 图像处理 Todo
                                //if(xobj.TryGetValue(PdfName.Key_SMask, out var maskRef))
                                //{
                                //    var maskObj = GetPdfFileObject<XObjectDictionary>(maskRef.ToString());
                                //    if(maskObj != null)
                                //    {
                                //        // 加载mask数据
                                //        Image? img = null;
                                //        if (xobj.Filters.Any(p => p == IFilter.Name_DCTDecode))
                                //        {
                                //            img = Image.Load(imgData);
                                //        }
                                //        else
                                //        {
                                //            var colors = imgData.Length / (xobj.Height * xobj.Width);
                                //            img = (colors * 8) switch
                                //            {
                                //                32 => Image.LoadPixelData<Rgba32>(imgData, xobj.Width, xobj.Height),
                                //                24 => Image.LoadPixelData<Rgb24>(imgData, xobj.Width, xobj.Height),
                                //                16 => Image.LoadPixelData<La16>(imgData, xobj.Width, xobj.Height),
                                //                8 => Image.LoadPixelData<L8>(imgData, xobj.Width, xobj.Height),
                                //                _ => null
                                //            };
                                //        }
                                //        if(img != null)
                                //        {
                                //            // 判断类型 全部转化为 Rgba32

                                //            // 前提蒙版图像一定是一个灰度图

                                //            // 循环遍历将其处理为一个
                                //        }
                                //    }
                                //}
                                // 
                                #endregion
                                if (xobj.Filters.Any(p => 
                                    p == IFilter.Name_CCITTFaxDecode 
                                    || p == IFilter.Name_CCITTFaxDecode2
                                    ))
                                {


                                    try
                                    {
                                        //using var fs = File.OpenWrite($"out/fp{page}.tiff");
                                        //fs.Write(imgData);
                                        //fs.Flush();
                                        using var img = Image.Load<Rgba32>(imgData);
                                        using var ms = new MemoryStream();
                                        img.SaveAsPng(ms);
                                        ms.Flush();
                                        imgData = ms.ToArray();
                                    }
                                    catch 
                                    {
                                        // 暂时发现ImageSharp不支持部分tiff格式的加载
                                        // 报错信息 ccitt extensions 1D codes are not supported.
                                        // https://github.com/SixLabors/ImageSharp/commit/6c439092fd5b89dfddd6b67e13be5c869652a0ad
                                        // https://github.com/SixLabors/ImageSharp/issues/2451

                                    }
                                }
                                else if (xobj.Filters.Any(p =>
                                    p == IFilter.Name_DCTDecode
                                    || p == IFilter.Name_DCTDecode2
                                ))
                                {
                                    using var ms = new MemoryStream();
                                    var img = Image.Load<Rgba32>(xobj.StreamData);
                                    img.SaveAsPng(ms);
                                    ms.Flush();
                                    imgData = ms.ToArray();
                                }
                                else if (xobj.Filters.Any(p => p == IFilter.Name_FlateDecode || p == IFilter.Name_FlateDecode2))
                                {
                                    // Flate Fl 原始像素数据
                                    var dp = new PredictorParam(xobj);
                                    var colors = imgData.Length / (xobj.Height * xobj.Width);

                                    Image? image2 = (colors * 8) switch
                                    {
                                        32 => Image.LoadPixelData<Rgba32>(imgData, xobj.Width, xobj.Height),
                                        24 => Image.LoadPixelData<Rgb24>(imgData, xobj.Width, xobj.Height),
                                        16 => Image.LoadPixelData<La16>(imgData, xobj.Width, xobj.Height),
                                        8 => Image.LoadPixelData<L8>(imgData, xobj.Width, xobj.Height),
                                        _ => null
                                    };
                                    if (image2 != null)
                                    {
                                        using var ms = new MemoryStream();
                                        image2.SaveAsPng(ms);
                                        ms.Flush();
                                        imgData = ms.ToArray();
                                    }
                                }

                                imgs.Add(imgData);
                            }
                        }
                    }
                }
            }

            return imgs.ToArray();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="page"></param>
        internal RenderEngine GetRenderEngine(Int32 page)
        {
            // 获取页对象
            if (page < 0 || page >= PageItems.Count)
            {
                return null;
            }
            var pageDic = PageItems[page];
            //GetPdfFileObject<PageDictionary>(id);
            if (pageDic == null)
            {
                return null;
            }
            var engine = new RenderEngine(pageDic);
            return engine;
        }

        //internal List<String> GetText(Int32 page)
        //{
        //    // 获取页对象
        //    if (page < 0 || page >= PageItems.Count)
        //    {
        //        return [];
        //    }
        //    var pageDic = PageItems[page];
        //    if (pageDic == null)
        //    {
        //        return [];
        //    }
        //    var engine = new RenderEngine(pageDic);
        //    var txt = engine.GetAllText();
        //    return txt;
        //}
    }
}
